#include "Node.h"

#include "RootNode.h"

namespace Rz { namespace Graphics { namespace Scene {

Node::Node()
	: Node(NodeType::None)
{

}

Node::Node(NodeType type)
	: _parent(nullptr)
	, _dirtyWorldMatrix(true)
	, _visible(true)
	, _nodeType(type)
{

}

const String& Node::GetNodeName() const
{
	return _nodeName;
}

void Node::SetNodeName(const String& name)
{
	_nodeName = name;
}

bool Node::IsVisible() const
{
	return _visible;
}

bool Node::IsRoot() const
{
	return _nodeType == NodeType::Root;
}

NodeType Node::GetNodeType() const
{
	return _nodeType;
}

bool Node::HasParent() const
{
	return !!_parent;
}

const Node* Node::GetParent() const
{
	return _parent;
}

Node* Node::GetParent()
{
	return _parent;
}

void Node::SetParent(Node* newParent)
{
	this->Ref();

	if (_parent)
	{
		_parent->_children.Remove(this);
	}

	_parent = newParent;

	if (_parent)
	{
		_parent->_children.Add(this);
	}

	this->MakeWorldMatrixDirty();

	this->Unref();
}

void Node::Add(Node* node)
{
	node->SetParent(this);
}

size_t Node::GetChildrenCount() const
{ 
	return _children.GetCount();
}

const Node::Collection Node::GetChildren() const
{
	return _children;
}

Node::Collection Node::GetChildren()
{
	return _children;
}

const Node* Node::GetChild(size_t index) const
{
	return _children[index];
}

Node* Node::GetChild(size_t index)
{
	return _children[index];
}

const RootNode* Node::GetRootNode() const
{
	return static_cast<const RootNode*>(this->GetRootNode());
}

RootNode* Node::GetRootNode()
{
	Node* result = this;

	for (;;)
	{
		if (result->IsRoot())
		{
			return result->As<RootNode*>();
		}
		else if (result->GetParent() == nullptr)
		{
			return nullptr;
		}

		result = result->GetParent();
	}
}

void Node::Show(bool show)
{
	_visible = show;
}

void Node::Hide()
{
	_visible = false;
}

const Math::Matrix4& Node::GetMatrix() const
{
	return _matrix;
}

void Node::SetMatrix(const Math::Matrix4& matrix)
{
	_matrix = matrix;
	MakeWorldMatrixDirty();
}

const Math::Matrix4& Node::GetWorldMatrix() const
{
	if (_dirtyWorldMatrix)
	{
		if (_parent)
		{
			_worldMatrix = _parent->GetWorldMatrix() * _matrix;
		}
		else
		{
			_worldMatrix = _matrix;
		}

		_dirtyWorldMatrix = false;
	}

	return _worldMatrix;
}
	
const Math::Matrix3& Node::GetNormalMatrix() const
{
	_normalMatrix = GetWorldMatrix().Inverted().Transposed().ToMatrix3();
	return _normalMatrix;
}

const Math::Matrix4& Node::GetInverseMatrix() const
{
	if (_dirtyWorldMatrix)
	{
		_inverseMatrix = GetWorldMatrix().Inverted();
	}

	return _inverseMatrix;
}

void Node::SetNodeType(NodeType nodeType)
{
	_nodeType = nodeType;
}

void Node::MakeWorldMatrixDirty()
{
	_dirtyWorldMatrix = true;

	for (auto& child : _children)
	{
		child->MakeWorldMatrixDirty();
	}
}

bool Node::IsDirty() const
{
	return _dirtyWorldMatrix;
}

} } }